import * as THREE from "three";
import {ThreeUtil} from "@/utils/ThreeUtil";
import { GLTFLoader } from "&t/examples/jsm/loaders/GLTFLoader";
import type { GLTF } from "&t/examples/jsm/loaders/GLTFLoader";
import {JoyStick} from "@/logic/gallery/Demo01/JoyStick";
import type {Object3D} from "three";

const options = {
    speed: 6,
    turnSpeed: 2,
    move: {
        forward: 0,
        turn: 0
    },
    colliders: Array<Object3D>(),
    debugMaterial: new THREE.MeshBasicMaterial({
        color:0xff0000,
        wireframe: true
    }),
    clock: new THREE.Clock(),
};

export class DemoInit {
    private readonly _threeUtil: ThreeUtil;
    private readonly _threeView: HTMLElement;


    constructor(threeUtil: ThreeUtil, threeView: HTMLElement) {
        this._threeUtil = threeUtil;
        this._threeView = threeView;
    }

    initData = (): void=> {
        this._threeUtil.renderer.outputColorSpace = THREE.SRGBColorSpace;
        this._threeUtil.renderer.setPixelRatio(window.devicePixelRatio);
        this._threeUtil.camera.position.set(-10, 2, 8);
        this._threeUtil.camera.fov = 90;
        this._threeUtil.camera.near = 0.1;
        this._threeUtil.camera.far = 5000;
        this._loadGLBModel1();
        this._loadGLBModel2();
        this._createPlayer();
        this._createCamera();
        this._createLights();
        this._createEvents();
        this._createJoyStick();
        this._threeUtil.renderer.setSize(this._threeView.offsetWidth - 15, this._threeView.offsetHeight - 15, true);
        this._threeView.appendChild( this._threeUtil.renderer.domElement );
        this._render();
    }

    // 加载模型
    private readonly _loadGLBModel1 = () => {
        const loader = new GLTFLoader();
        loader.load(`/src/assets/gallery/Demo01/model/gallery.glb`, (gltf: GLTF) => {
            gltf.scene.traverse((child: Object3D) => {
                switch(child.name) {
                    case 'walls':
                        this._initWalls( child );
                        break
                    case 'stairs':
                        this._initStairs( child );
                        break
                }
                //设置展画边框贴图
                if(child.name.includes('paint')) {
                    this._initFrames( child );
                }
                //设置展画图片贴图
                if(child.name.includes('draw')) {
                    this._initDraws( child );
                }
            })
            this._threeUtil.scene.add(gltf.scene);
        })
    }

    private readonly _loadGLBModel2 = () => {
        const loader = new GLTFLoader();
        loader.load(`/src/assets/gallery/Demo01/model/collider.glb`, (gltf: GLTF) => {
            gltf.scene.traverse(( child: Object3D ) => {
                if(child.name.includes('collider')) {
                    options.colliders.push( child );
                }
            });
            options.colliders.forEach( ( item: Object3D ) => {
                item.visible = false;
                this._threeUtil.scene.add( item );
            })
        })
    }

    private readonly _createPlayer = () => {
        const geometry = new THREE.BoxGeometry(1, 2, 1)
        const material = new THREE.MeshBasicMaterial({
            color: 0xff0000,
            wireframe: true
        })
        this._threeUtil.items['player'] = new THREE.Mesh(geometry, material);
        this._threeUtil.items['player'].name = 'player';
        geometry.translate(0, 1, 0);
        this._threeUtil.items['player'].position.set(-5, 0, 5);
        this._threeUtil.scene.add( this._threeUtil.items['player'] );
    }

    private _createCamera = () => {
        const back = new THREE.Object3D();
        back.position.set(0, 2, 1);
        back.parent = this._threeUtil.items['player'];
        this._threeUtil.items['activeCamera'] = back;
    }

    private _createLights = () => {
        this._threeUtil.items['ambientLight'] = new THREE.AmbientLight(0xe0ffff, 0.6);
        this._threeUtil.scene.add( this._threeUtil.items['ambientLight'] );

        this._threeUtil.items['pointLight1'] = new THREE.PointLight(0xe0ffff, 0.1, 20);
        this._threeUtil.items['pointLight1'].position.set(-2, 3, 2);
        this._threeUtil.scene.add( this._threeUtil.items['pointLight1'] );

        this._threeUtil.items['pointLight2'] = new THREE.PointLight(0xe0ffff, 0.1, 20)
        this._threeUtil.items['pointLight2'].position.set(0, 3, -6)
        this._threeUtil.scene.add(this._threeUtil.items['pointLight2'])

        this._threeUtil.items['pointLight3'] = new THREE.PointLight(0xe0ffff, 0.1, 20)
        this._threeUtil.items['pointLight3'].position.set(-12, 3, 6)
        this._threeUtil.scene.add(this._threeUtil.items['pointLight3'])

        this._threeUtil.items['pointLight4'] = new THREE.PointLight(0xe0ffff, 0.1, 20)
        this._threeUtil.items['pointLight4'].position.set(-12, 4, -4)
        this._threeUtil.scene.add(this._threeUtil.items['pointLight4'])

        this._threeUtil.items['pointLight5'] = new THREE.PointLight(0xe0ffff, 0.1, 20)
        this._threeUtil.items['pointLight5'].position.set(12, 4, -8)
        this._threeUtil.scene.add(this._threeUtil.items['pointLight5'])

        this._threeUtil.items['pointLight6'] = new THREE.PointLight(0xe0ffff, 0.1, 20)
        this._threeUtil.items['pointLight6'].position.set(12, 4, 0)
        this._threeUtil.scene.add(this._threeUtil.items['pointLight6'])

        this._threeUtil.items['pointLight7'] = new THREE.PointLight(0xe0ffff, 0.1, 20)
        this._threeUtil.items['pointLight7'].position.set(12, 4, 8)
        this._threeUtil.scene.add(this._threeUtil.items['pointLight7'])
    }

    private _createJoyStick() {
        const that = this;
        new JoyStick({
            onMove: (forward: number, turn: number) => {
                turn = -turn;
                if(Math.abs(forward) < 0.3) {
                    forward = 0;
                }
                if(Math.abs(turn) < 0.1) {
                    turn = 0;
                }
                options.move.forward = forward;
                options.move.turn = turn;
            }
        })
    }

    private _render = () => {
        this._threeUtil.renderer.render( this._threeUtil.scene, this._threeUtil.camera );
    }

    public animateUpdate = () => {
        const dt = options.clock.getDelta();
        this._updatePlayer(dt);
        this._updateCamera(dt);
    }

    private _updatePlayer = (dt: number) => {
        const pos = this._threeUtil.items['player'].position.clone();
        pos.y += 2;
        const dir = new THREE.Vector3();
        this._threeUtil.items['player'].getWorldDirection(dir);
        dir.negate();

        if (options.move.forward < 0) {
            dir.negate();
        }
        const raycaster = new THREE.Raycaster(pos, dir);
        let blocked = false;

        if(options.colliders.length > 0) {
            const intersect = raycaster.intersectObjects( options.colliders );
            if (intersect.length > 0) {
                if (intersect[0].distance < 1) {
                    blocked = true;
                }
            }
        }
        if(!blocked) {
            if(options.move.forward !== 0) {
                if (options.move.forward > 0) {
                    this._threeUtil.items['player'].translateZ(-dt * options.speed);
                } else {
                    this._threeUtil.items['player'].translateZ(dt * options.speed * 0.5);
                }
            }
        }

        if(options.move.turn !== 0) {
            this._threeUtil.items['player'].rotateY(options.move.turn * dt);
        }
    }

    private _updateCamera = (dt: number) => {
        //更新摄像机
        this._threeUtil.camera.position.lerp(
            this._threeUtil.items['activeCamera'].getWorldPosition(
                new THREE.Vector3()
            ),
            0.08
        );
        const pos = this._threeUtil.items['player'].position.clone();
        pos.y += 2;
        this._threeUtil.camera.lookAt(pos);
    }

    private _initDraws(child: Object3D) {
        const index = child.name.split('draw')[1];
        const texture =  new THREE.TextureLoader().load(`/src/assets/gallery/Demo01/img/${index}.jpg`);
        texture.colorSpace = THREE.SRGBColorSpace;
        texture.flipY = false;
        const material = new THREE.MeshPhongMaterial({
            map: texture
        });
        child.material = material;
    }

    private _initFrames(child: Object3D) {
        child.material = new THREE.MeshBasicMaterial({
            color: 0x7f5816
        })
    }

    private _initStairs = (child: Object3D) => {
        const material = new THREE.MeshStandardMaterial({
            color: 0xd1cdb7
        });
        material.roughness = 0.5;
        material.metalness = 0.6;
        child.material = material;
    }

    private _initWalls = (child: Object3D) => {
        const material = new THREE.MeshStandardMaterial({
            color: 0xffffff
        });
        material.roughness = 0.5;
        material.metalness = 0.6;
        child.material = material;
    }

    private _createEvents() {
        document.addEventListener('keydown', this._onKeyDown);
        document.addEventListener('keyup', this._onKeyUp);
    }

    private _onKeyDown(event: KeyboardEvent) {
        switch ( event.code ) {
            case 'ArrowUp':
            case 'KeyW':
                options.move.forward = 1;
                break

            case 'ArrowLeft':
            case 'KeyA':
                options.move.turn = options.turnSpeed;
                break

            case 'ArrowDown':
            case 'KeyS':
                options.move.forward = -1;
                break

            case 'ArrowRight':
            case 'KeyD':
                options.move.turn = -options.turnSpeed;
                break
            case 'Space':
                break
        }
    }

    private _onKeyUp(event: KeyboardEvent) {
        const that = this;
        switch ( event.code ) {
            case 'ArrowUp':
            case 'KeyW':
                options.move.forward = 0;
                break

            case 'ArrowLeft':
            case 'KeyA':
                options.move.turn = 0;
                break

            case 'ArrowDown':
            case 'KeyS':
                options.move.forward = 0;
                break

            case 'ArrowRight':
            case 'KeyD':
                options.move.turn = 0;
                break

        }
    }
}